<template>
  <div class="step-nlbp">
    <div id="nlbp-stencil" :style="{ display: isView ? 'none' : '' }"></div>
    <div id="nlbp-container"></div>
    <div class="nlbp-draw">
      <component :is="fbOptions.flow.currentShape" :fbOptions="fbOptions"/>
    </div>
  </div>
</template>

<script>
import {getNLBP} from "@slc/api/slc/inventory";
import {Graph, Dom} from "@antv/x6";
import {Stencil} from "@antv/x6-plugin-stencil";
import {Snapline} from "@antv/x6-plugin-snapline";
import {Selection} from "@antv/x6-plugin-selection";
import {Keyboard} from "@antv/x6-plugin-keyboard";
import {Clipboard} from "@antv/x6-plugin-clipboard";
import {History} from "@antv/x6-plugin-history";

import {ports, graphOptions} from "../../config/x6";
import {
  defaultEdge,
  startInfo,
  endInfo,
  soapInfo,
  restfulInfo,
  jmsInfo,
} from "../../config/deafultDsl";
import {deepClone} from "@/utils/index";
import CustomStart from "./custom-start.vue";
import CustomTransform from "./custom-transform.vue";
import CustomEndpoint from "./custom-endpoint.vue";
import CustomApiport from "./custom-apiport.vue";
import CustomApiportDataset from "./custom-apiport-dataset.vue";
import CustomDistribute from "./custom-distribute.vue";
import CustomLine from "./custom-line.vue";
import CustomRaw from "./custom-raw.vue";
import CustomBuilder from "./custom-builder.vue";

let stencil = null;
export default {
  props: {
    fbOptions: {
      type: Object,
      default: () => {
      }
    },
  },
  components: {
      CustomStart,
      CustomTransform,
      CustomEndpoint,
      CustomApiport,
      CustomApiportDataset,
      CustomDistribute,
      CustomLine,
      CustomRaw,
      CustomBuilder,
    // CustomStart: () => import("./custom-start.vue"),
    // CustomTransform: () => import("./custom-transform.vue"),
    // CustomEndpoint: () => import("./custom-endpoint.vue"),
    // CustomApiport: () => import("./custom-apiport.vue"),
    // CustomApiportDataset: () => import("./custom-apiport-dataset.vue"),
    // CustomDistribute: () => import("./custom-distribute.vue"),
    // CustomLine: () => import("./custom-line.vue"),
    // CustomRaw: () => import("./custom-raw.vue"),
    // CustomBuilder: () => import("./custom-builder.vue"),
  },
  computed: {
    isView() {
      return (
        this.fbOptions.operationType === "view"
      );
    },
  },
  async mounted() {
    this.init();
    await  this.handleDefaultShow();
  },
  activated() {
    this.handleDefaultShow();
  },
  beforeUnmount() {
    stencil = null;
  },
  methods: {
    // 点击节点
    hanldeCellClick(cell) {
      console.log(cell.id, cell.shape);
      // return
      this.fbOptions.flow.currentNodeId = cell.id;
      this.fbOptions.flow.selectNode = cell;
      if (cell.isNode()) {
        if (cell.shape === "custom-end" || cell.shape === "custom-controller") {
          this.fbOptions.flow.currentShape = "custom-start";


        } else {
          this.fbOptions.flow.currentShape = cell.shape;
        }
      } else if (cell.isEdge()) {
        // 起始点为条件选择的连接线
        let source = cell.getSourceCell() || {};
        if (source.data && source.data.type === "controller-choice") {
          this.fbOptions.flow.currentShape = "custom-line";
        } else {
          this.fbOptions.flow.currentShape = "custom-start";
        }
      }
    },
    // 添加节点
    handleNodeAdd(node) {
      let tips;
      switch (node.shape) {
        case 'custom-start':
          tips = "只能同时存在1个开始节点！"
          break;
        case 'custom-end':
          tips = "只能同时存在1个结束节点！"
          break;
        case 'custom-endpoint':
          tips = "只能同时存在1个发布端点控件！"
          break;
        default:
          break;
      }
      if (tips) {
        this.$message.error(tips);
        this.fbOptions.flow.graph.removeCell(node);
      } else {
        this.fbOptions.flow.moreNodeInfo = {
          ...this.fbOptions.flow.moreNodeInfo,
          [node.id]: {},
        };
        this.hanldeCellClick(node);
      }
    },
    // 删除节点
    hanldeNodeDelete(node) {
      const obj = deepClone(this.fbOptions.flow.moreNodeInfo);
      delete obj[node.id];
      this.fbOptions.flow.moreNodeInfo = obj;
    },

    checkEdgeConnected(edge, currentCell) {
      const { relationship = [], type: currentType } = edge.getSourceNode().data;
      const { type: nextType } = edge.getTargetNode().data;
      const extraType = [
        'controller-choice',
        'controller-choice-closed',
        'controller-multicast',
        'controller-multicast-closed',
        'processor-ParamBuilderJson',
      ];
      const preNode = this.fbOptions.flow.graph.getPredecessors(currentCell);
      const errorMessages = {
        multicastError: '连接【分发汇聚】,前置环节必须配置【多路分发】节点！',
        multicastNextError:'流程节点存在【多路分发】，未【分发汇聚】无法结束！',
        choiceError: '连接【选择闭合】,前置环节必须配置【条件选择】节点！',
        choiceNextError:'流程节点存在【条件选择】，未【选择闭合】无法结束！',
        connectionError: '连接规则不正确，请连接正确的后续环节！',
        connectionNodeError: '该节点不支持多个出口，请选择正确的出口连接',


      };

      if (nextType === 'controller-multicast-closed') {
        const nodeMulticast = preNode.find(e => e.data.type === 'controller-multicast');
        if (!nodeMulticast) {
          this.$message.error(errorMessages.multicastError);
          return false;
        } else {
          // 获取多路分发上一节点
          const lastNodeMulticast = this.fbOptions.flow.graph.getNeighbors(nodeMulticast, { incoming: true })[0] || {};
          if (['from-rest', 'from-soap', 'from-jms'].includes(lastNodeMulticast.data.type)) {
            if (['to-rest', 'processor-SoapXmlToJson', 'to-soap', 'processor-JsonToSoapXml', 'to-jms','to-dataset'].includes(currentType)) {
              relationship.push('controller-multicast-closed');
            }
          }
        }
      }



      const sourceNodeId = edge.getSourceCell().id;
      const connectedEdges = this.fbOptions.flow.graph.getEdges().filter(e => e.getSourceCell().id === sourceNodeId);
      if((currentType!=='controller-multicast' && currentType!=='controller-choice' ) && connectedEdges.length > 1) {
        this.$message.error(errorMessages.connectionNodeError);
        return false;
      }


      if (nextType === 'controller-choice-closed' && !preNode.find(e => e.data.type === 'controller-choice')) {
        this.$message.error(errorMessages.choiceError);
        return false;
      }

      if(nextType === 'end' && preNode.find(e => e.data.type === 'controller-multicast') && !preNode.find(e => e.data.type === 'controller-multicast-closed')){
        this.$message.error(errorMessages.multicastNextError);
        return false;
      }



      if(nextType === 'end' && preNode.find(e => e.data.type === 'controller-choice') && !preNode.find(e => e.data.type === 'controller-choice-closed')){
        this.$message.error(errorMessages.choiceNextError);
        return false;
      }



      if ((relationship.length > 0 && relationship.includes(nextType)) || extraType.includes(currentType)) {
        return true;
      } else {
        this.$message.error(errorMessages.connectionError);
        return false;
      }

    },

    // 连接边
    handleEdgeConnected(edge, currentCell) {



      let flag = !this.checkEdgeConnected(edge, currentCell);
      if (flag) {
        this.fbOptions.flow.graph.removeEdge(edge);
        return
      }
      const source = edge.getSourceCell();
      if (source.data && source.data.type === "controller-choice") {
        this.fbOptions.flow.moreNodeInfo = {
          ...this.fbOptions.flow.moreNodeInfo,
          [edge.id]: {},
        }
      }
      // 处理 J转S S转J 的节点数据
      this.handleTransformData(edge)
      // 处理 结构转换节点 的数据
      this.handleBuilderData()

    },
    // 初始化内容
    async handleDefaultShow() {
      let graphData;
      const res = await getNLBP({serviceVerId: this.fbOptions.formData.serviceVerId});
      if (res.code === "200") {
        if (!this.isView) {
          this.fbOptions.flow.serviceDomain = {
            ...this.fbOptions.formData,
            paramIn: this.fbOptions.inputData,
          }
        }
        if (res.data && res.data.contentText) {
          const contentText = JSON.parse(res.data.contentText);
          console.log("contentText", contentText);
          const {properties = {}} = contentText;
          const {serviceDomain = {}, moreNodeInfo = {}} = properties;
          graphData = {cells: contentText.cells.map((cell) => cell || {})};
          if (this.isView) {
            this.fbOptions.flow.serviceDomain = {
              ...serviceDomain,
            };
          }

          this.fbOptions.flow.moreNodeInfo = {
            ...moreNodeInfo,
          }
        } else {
          let endpoint;
          switch (this.fbOptions.formData.serviceType) {
            case "1":
              endpoint = soapInfo;
              break;
            case "2":
              endpoint = restfulInfo;
              break;
            case "3":
              endpoint = jmsInfo;
              break;
            default:
              break;
          }
          console.log(startInfo)
          graphData = {
            cells: [defaultEdge, startInfo, endpoint, endInfo],
          };
        }
      }
      this.fbOptions.flow.graph.clearCells();
      this.fbOptions.flow.graph.fromJSON(graphData);
      this.fbOptions.flow.currentShape = "custom-start";

    },
    // 处理 J转S S转J 的节点数据
    handleTransformData(edge) {
      const hanldeSoapDirection = (arr, type) => {
        arr.forEach(n => {
          const predecessors = this.fbOptions.flow.graph.getPredecessors(n)
          const successors = this.fbOptions.flow.graph.getSuccessors(n)
          console.log('predecessors', predecessors)
          if (predecessors.length > 0) {
            const hasTo = predecessors.reverse().find(n => n.data.type === `to-${type}`)
            const hasFrom = predecessors.find(n => n.data.type === `from-${type}`)
            let soap_direction
            if (hasTo) {
              soap_direction = 'response'
            } else if (!hasTo && hasFrom) {
              soap_direction = 'requset'
            }
            this.fbOptions.flow.moreNodeInfo = {
              ...this.fbOptions.flow.moreNodeInfo,
              [n.id]: {
                ...this.fbOptions.flow.moreNodeInfo[n.id],
                soap_direction
              },
            }

          }
        })
      }
      const j2sArr = this.fbOptions.flow.graph.getNodes().filter(n => n.data.type === 'processor-JsonToSoapXml')
      const s2jArr = this.fbOptions.flow.graph.getNodes().filter(n => n.data.type === 'processor-SoapXmlToJson')
      if (j2sArr.length > 0) {
        hanldeSoapDirection(j2sArr, 'rest')
      }
      if (s2jArr.length > 0) {
        hanldeSoapDirection(s2jArr, 'soap')
      }
      const source = edge.getSourceCell();
      const target = edge.getTargetCell();
      if (source.data.type === "processor-JsonToSoapXml") {
        if (target.data.type === "to-soap") {
          this.fbOptions.flow.moreNodeInfo = {
            ...this.fbOptions.flow.moreNodeInfo,
            [source.id]: {
              ...this.fbOptions.flow.moreNodeInfo[source.id],
              soapId: target.id,
            },
          }
        }
      }
      if (target.data.type === "processor-SoapXmlToJson") {
        if (source.data.type === "to-soap") {
          this.fbOptions.flow.moreNodeInfo = {
            ...this.fbOptions.flow.moreNodeInfo,
            [target.id]: {
              ...this.fbOptions.flow.moreNodeInfo[target.id],
              soapId: source.id,
            },
          }

        }
      }
    },
    // 处理 结构转换节点 的数据
    handleBuilderData() {
      // 1.每次触发连线，判断所有节点中是否含有 结构转换（custom-builder） 节点
      const builders = this.fbOptions.flow.graph.getNodes().filter(n => n.shape === 'custom-builder') || []
      if (builders.length > 0) {
        // 2. 遍历，找出每个custom-builder节点之前的所有连接节点
        builders.forEach(n => {
          const predecessors = this.fbOptions.flow.graph.getPredecessors(n)
          const nextNode = this.fbOptions.flow.graph.getNeighbors(n, {outgoing: true})[0] || {}
          if (predecessors.length > 0) {
            const preApiport = predecessors.find(n => n.shape.indexOf('custom-apiport') !=-1)
            const preEndpoint = predecessors.find(n => n.shape === 'custom-endpoint')
            // 需要从前面接口节点取值，保存节点ID
            let preId = []
            if (preApiport) { // 3.判断前面的节点是否含有 接口节点（custom-apiport）
              preId = preApiport.id
            } else if (!preApiport && preEndpoint) { // 4. 如果没有接口节点，则取端点节点（custom-endpoint）
              preId = 'serviceDomain'
            }
            this.fbOptions.flow.moreNodeInfo = {
              ...this.fbOptions.flow.moreNodeInfo,
              [n.id]: {
                ...this.fbOptions.flow.moreNodeInfo[n.id],
                preId
              },
            }

          }
          // 需要根据下一个相邻节点中的数据调用接口获取输出参数
          if (nextNode.shape.indexOf('custom-apiport') !=-1) {
            this.fbOptions.flow.moreNodeInfo = {
              ...this.fbOptions.flow.moreNodeInfo,
              [n.id]: {
                ...this.fbOptions.flow.moreNodeInfo[n.id],
                nextId: nextNode.id
              },
            }

          }
        })
      }
    },
    /**
     * 初始化画布
     */
    init() {
      console.log('isView', this.isView);
      const graph = new Graph(graphOptions(this.isView, "nlbp-container"));
      this.fbOptions.flow.graph = graph;
      // 注册插件
      this.initGraphUse();
      // 注册自定义节点
      this.registerNode();
      if (!this.isView) {
        // 初始化左侧目录
        this.initStencil();
        // 初始化目录节点
        this.loadStencilNode();
        // 注册事件
      }
      this.initEvent();
    },
    /**
     * 初始化注册功能
     */
    initGraphUse() {
      this.fbOptions.flow.graph
        .use(
          new Selection({
            enabled: true,
            // rubberband: true,//框选
            // showNodeSelectionBox: true,
          })
        )
        .use(
          new Snapline({
            enabled: true,
          })
        )
        .use(
          new Keyboard({
            enabled: true,
          })
        )
        .use(
          new Clipboard({
            enabled: true,
          })
        )
        .use(
          new History({
            enabled: true,
          })
        );
    },
    /**
     * 初始化左侧目录
     */
    initStencil() {
      stencil = new Stencil({
        title: "DSL编排器",
        target: this.fbOptions.flow.graph,
        stencilGraphWidth: 200,
        stencilGraphHeight: 0,
        collapsable: true,
        groups: [
          {
            title: "开始结束控件",
            name: "group1",
            graphHeight: 70,
          },
          {
            title: "发布端点控件",
            name: "group2",
            graphHeight: 120,
          },
          {
            title: "API接口控件",
            name: "group3",
            graphHeight: 120,
          },
          {
            title: "转换控制",
            name: "group4",
            graphHeight: 165,
          },
          {
            title: "控制节点",
            name: "group5",
            graphHeight: 120,
          },
        ],
        layoutOptions: {
          columns: 2,
          columnWidth: 80,
          rowHeight: 50,
        },
      });
      document.getElementById("nlbp-stencil").appendChild(stencil.container);
    },
    /**
     * 创建左侧菜单中的节点
     */
    loadStencilNode() {
      const start = this.fbOptions.flow.graph.createNode({
        shape: "custom-start",
        label: "开始",
        data: {
          type: "start",
          relationship: ['from-soap', 'from-rest', 'from-jms','controller-choice-closed'],
        },
      });
      const end = this.fbOptions.flow.graph.createNode({
        shape: "custom-end",
        label: "结束",
        data: {
          type: "end",
          relationship: []
        },
      });
      const endpointSoap = this.fbOptions.flow.graph.createNode({
        shape: "custom-endpoint",
        label: "SOAP",
        attrs: {
          ".rank": {
            text: Dom.breakText('SOAP', {width: 160, height: 45}),
          },
          ".name": {
            text: Dom.breakText('端点', {width: 160, height: 45}),
          },
        },
        data: {
          type: "from-soap",
          relationship: ['to-soap', 'processor-SoapXmlToJson', 'controller-choice', 'controller-multicast','controller-choice-closed']
        },
      });
      const endpointRestful = this.fbOptions.flow.graph.createNode({
        shape: "custom-endpoint",
        label: "Restful",
        attrs: {
          ".rank": {
            text: Dom.breakText('Restful', {width: 160, height: 45}),
          },
          ".name": {
            text: Dom.breakText('端点', {width: 160, height: 45}),
          },
        },
        data: {
          type: "from-rest",
          relationship: ['to-rest', 'processor-JsonToSoapXml', 'processor-RawToFormData', 'processor-ParamBuilderJson', 'controller-choice', 'controller-multicast', "to-dataset",'controller-choice-closed']
        },
      });
      const endpointJms = this.fbOptions.flow.graph.createNode({
        shape: "custom-endpoint",
        label: "JMS",
        attrs: {
          ".rank": {
            text: Dom.breakText('JMS', {width: 160, height: 45}),
          },
          ".name": {
            text: Dom.breakText('端点', {width: 160, height: 45}),
          },
        },
        data: {
          type: "from-jms",
          relationship: ['to-jms','controller-choice-closed']
        },
      });
      const apiSoap = this.fbOptions.flow.graph.createNode({
        shape: "custom-apiport",
        // label: "SOAP接口",
        attrs: {
          ".rank": {
            text: Dom.breakText('SOAP', {width: 160, height: 45}),
          },
          ".name": {
            text: Dom.breakText('接口', {width: 160, height: 45}),
          },
        },
        data: {
          type: "to-soap",
          apiType: "1",
          relationship: ['processor-SoapXmlToJson', 'controller-choice', 'controller-multicast', 'end','controller-choice-closed']

        },
      });
      const apiRestful = this.fbOptions.flow.graph.createNode({
        shape: "custom-apiport",
        // label: "Restful",
        attrs: {
          ".rank": {
            text: Dom.breakText('Restful', {width: 160, height: 45}),
          },
          ".name": {
            text: Dom.breakText('接口', {width: 160, height: 45}),
          },
        },
        data: {
          type: "to-rest",
          apiType: "2",
          relationship: ['processor-JsonToSoapXml', 'processor-ParamBuilderJson', 'processor-JsonResponseWrapper', 'controller-choice', 'controller-multicast', 'end','controller-choice-closed']

        },
      });
      const apiJms = this.fbOptions.flow.graph.createNode({
        shape: "custom-apiport",
        // label: "JMS",
        attrs: {
          ".rank": {
            text: Dom.breakText('JMS', {width: 160, height: 45}),
          },
          ".name": {
            text: Dom.breakText('接口', {width: 160, height: 45}),
          },
        },
        data: {
          type: "to-jms",
          apiType: "3",
          relationship: ['end','controller-choice-closed']

        },
      });
      const apiDataSet = this.fbOptions.flow.graph.createNode({
        shape: "custom-apiport-dataset",
        // label: "DataSet",
        attrs: {
          ".rank": {
            text: Dom.breakText('DataSet', {width: 160, height: 45}),
          },
          ".name": {
            text: Dom.breakText('接口', {width: 160, height: 45}),
          },
        },
        data: {
          type: "to-dataset",
          relationship: ['end', 'processor-JsonResponseWrapper','controller-choice-closed']

        },
      });
      const jsonToSoap = this.fbOptions.flow.graph.createNode({
        width: 70,
        height: 36,
        attrs: {
          text: {
            fontSize: 10,
            fill: "#262626",
          },
        },
        shape: "custom-transform",
        label: "JSON转SOAP",
        data: {
          type: "processor-JsonToSoapXml",
          inAgreement: "JSON",
          targetAgreement: "SOAP",
          relationship: ['to-soap', 'end','controller-choice-closed']

        },
      });
      const soapToJson = this.fbOptions.flow.graph.createNode({
        width: 70,
        height: 36,
        attrs: {
          text: {
            fontSize: 10,
            fill: "#262626",
          },
        },
        shape: "custom-transform",
        label: "SOAP转JSON",
        data: {
          type: "processor-SoapXmlToJson",
          inAgreement: "SOAP",
          targetAgreement: "JSON",
          relationship: ['to-rest', 'end','controller-choice-closed']

        },
      });
      const choice = this.fbOptions.flow.graph.createNode({
        shape: "custom-controller",
        label: "条件选择",
        data: {
          type: "controller-choice",
          relationship: []
        },
      });
      const choiceClosed = this.fbOptions.flow.graph.createNode({
        shape: "custom-controller",
        label: "选择闭合",
        data: {
          type: "controller-choice-closed",
          relationship: []
        },
      });
      const multicast = this.fbOptions.flow.graph.createNode({
        shape: "custom-distribute",
        label: "多路分发",
        data: {
          type: "controller-multicast",
          relationship: ['controller-choice-closed']

        },
      });
      const multicastClosed = this.fbOptions.flow.graph.createNode({
        shape: "custom-controller",
        label: "分发汇聚",
        data: {
          type: "controller-multicast-closed",
          relationship: ['controller-choice-closed']

        },
      });
      const rawToForm = this.fbOptions.flow.graph.createNode({
        width: 70,
        height: 36,
        shape: "custom-raw",
        label: "Raw转Form",
        data: {
          type: "processor-RawToFormData",
          relationship: ['to-rest','controller-choice-closed']
        },
      });
      const builder = this.fbOptions.flow.graph.createNode({
        width: 70,
        height: 36,
        shape: "custom-builder",
        label: "结构转换",
        data: {
          type: "processor-ParamBuilderJson",
          relationship: ['controller-choice-closed']
        },
      });
      const resPackage = this.fbOptions.flow.graph.createNode({
        width: 70,
        height: 36,
        shape: "custom-controller",
        label: "响应封装",
        data: {
          type: "processor-JsonResponseWrapper",
          relationship: ['to-soap', 'to-rest', 'to-jms', 'to-dataset', 'end','controller-choice-closed']

        },
      });
      stencil.load([start, end], "group1");
      stencil.load([endpointSoap, endpointRestful, endpointJms], "group2");
      stencil.load([apiSoap, apiRestful, apiJms, apiDataSet], "group3");
      stencil.load(
        [jsonToSoap, soapToJson, rawToForm, builder, resPackage],
        "group4"
      );
      stencil.load(
        [choice, choiceClosed, multicast, multicastClosed],
        "group5"
      );
    },
    /**
     * 注册自定义节点
     */
    registerNode() {
      Graph.registerNode(
        "custom-transform",
        {
          inherit: "rect",
          width: 66,
          height: 36,
          attrs: {
            body: {
              strokeWidth: 1,
              stroke: "#5F95FF",
              fill: "#EFF4FF",
              rx: 6,
              ry: 6,
            },
            text: {
              fontSize: 12,
              fill: "#262626",
            },
          },
          ports: {...ports},
        },
        true
      );
      Graph.registerNode(
        "custom-apiport",
        {
          inherit: "rect",
          width: 66,
          height: 36,
          markup: [
            {
              tagName: "rect",
              attrs: {
                class: "card",
              },
            },
            {
              tagName: "text",
              attrs: {
                class: "rank",
              },
            },
            {
              tagName: "text",
              attrs: {
                class: "name",
              },
            },
          ],
          attrs: {
            ".card": {
              rx: 10,
              ry: 10,
              refWidth: "100%",
              refHeight: "100%",
              fill: "#EFF4FF",
              stroke: "#5F95FF",
              strokeWidth: 1,
              pointerEvents: "visiblePainted",
            },
            ".rank": {
              refX: 0.5,
              refY: 0.3,
              fill: "#262626",
              fontSize: 12,
              textAnchor: "middle",
              textVerticalAnchor: "middle",
            },
            ".name": {
              refX: 0.5,
              refY: 0.7,
              fill: "#262626",
              fontSize: 12,
              textAnchor: "middle",
            },
          },
          ports: {...ports},
        },
        true
      );
      Graph.registerNode(
        "custom-apiport-dataset",
        {
          inherit: "rect",
          width: 66,
          height: 36,
          markup: [
            {
              tagName: "rect",
              attrs: {
                class: "card",
              },
            },
            {
              tagName: "text",
              attrs: {
                class: "rank",
              },
            },
            {
              tagName: "text",
              attrs: {
                class: "name",
              },
            },
          ],
          attrs: {
            ".card": {
              rx: 10,
              ry: 10,
              refWidth: "100%",
              refHeight: "100%",
              fill: "#EFF4FF",
              stroke: "#5F95FF",
              strokeWidth: 1,
              pointerEvents: "visiblePainted",
            },
            ".rank": {
              refX: 0.5,
              refY: 0.3,
              fill: "#262626",
              fontSize: 12,
              textAnchor: "middle",
              textVerticalAnchor: "middle",
            },
            ".name": {
              refX: 0.5,
              refY: 0.7,
              fill: "#262626",
              fontSize: 12,
              textAnchor: "middle",
            },
          },
          ports: {...ports},
        },
        true
      );
      Graph.registerNode(
        "custom-endpoint",
        {
          inherit: "rect",
          width: 66,
          height: 36,
          markup: [
            {
              tagName: "rect",
              attrs: {
                class: "card",
              },
            },
            {
              tagName: "text",
              attrs: {
                class: "rank",
              },
            },
            {
              tagName: "text",
              attrs: {
                class: "name",
              },
            },
          ],
          attrs: {
            ".card": {
              rx: 10,
              ry: 10,
              refWidth: "100%",
              refHeight: "100%",
              fill: "#EFF4FF",
              stroke: "#5F95FF",
              strokeWidth: 1,
              pointerEvents: "visiblePainted",
            },
            ".rank": {
              refX: 0.5,
              refY: 0.3,
              fill: "#262626",
              fontSize: 12,
              textAnchor: "middle",
              textVerticalAnchor: "middle",
            },
            ".name": {
              refX: 0.5,
              refY: 0.7,
              fill: "#262626",
              fontSize: 12,
              textAnchor: "middle",
            },
          },
          ports: {...ports},
        },
        true
      );
      Graph.registerNode(
        "custom-rect",
        {
          inherit: "rect",
          width: 66,
          height: 36,
          attrs: {
            body: {
              strokeWidth: 1,
              stroke: "#5F95FF",
              fill: "#EFF4FF",
            },
            text: {
              fontSize: 12,
              fill: "#262626",
            },
          },
          ports: {...ports},
        },
        true
      );

      Graph.registerNode(
        "custom-polygon",
        {
          inherit: "polygon",
          width: 66,
          height: 36,
          attrs: {
            body: {
              strokeWidth: 1,
              stroke: "#5F95FF",
              fill: "#EFF4FF",
            },
            text: {
              fontSize: 12,
              fill: "#262626",
            },
          },
          ports: {
            ...ports,
            items: [
              {
                group: "top",
              },
              {
                group: "bottom",
              },
            ],
          },
        },
        true
      );

      Graph.registerNode(
        "custom-start",
        {
          inherit: "circle",
          width: 45,
          height: 45,
          attrs: {
            body: {
              strokeWidth: 1,
              stroke: "#5F95FF",
              fill: "#EFF4FF",
            },
            text: {
              fontSize: 12,
              fill: "#262626",
            },
          },
          ports: {...ports},
        },
        true
      );
      Graph.registerNode(
        "custom-end",
        {
          inherit: "circle",
          width: 45,
          height: 45,
          attrs: {
            body: {
              strokeWidth: 1,
              stroke: "#5F95FF",
              fill: "#EFF4FF",
            },
            text: {
              fontSize: 12,
              fill: "#262626",
            },
          },
          ports: {...ports},
        },
        true
      );
      Graph.registerNode(
        "custom-controller",
        {
          inherit: "rect",
          width: 66,
          height: 36,
          attrs: {
            body: {
              strokeWidth: 1,
              stroke: "#5F95FF",
              fill: "#EFF4FF",
              rx: 6,
              ry: 6,
            },
            text: {
              fontSize: 12,
              fill: "#262626",
            },
          },
          ports: {...ports},
        },
        true
      );
      Graph.registerNode(
        "custom-distribute",
        {
          inherit: "rect",
          width: 66,
          height: 36,
          attrs: {
            body: {
              strokeWidth: 1,
              stroke: "#5F95FF",
              fill: "#EFF4FF",
              rx: 6,
              ry: 6,
            },
            text: {
              fontSize: 12,
              fill: "#262626",
            },
          },
          ports: {...ports},
        },
        true
      );
      Graph.registerNode(
        "custom-raw",
        {
          inherit: "rect",
          width: 66,
          height: 36,
          attrs: {
            body: {
              strokeWidth: 1,
              stroke: "#5F95FF",
              fill: "#EFF4FF",
              rx: 6,
              ry: 6,
            },
            text: {
              fontSize: 12,
              fill: "#262626",
            },
          },
          ports: {...ports},
        },
        true
      );
      Graph.registerNode(
        "custom-builder",
        {
          inherit: "rect",
          width: 66,
          height: 36,
          attrs: {
            body: {
              strokeWidth: 1,
              stroke: "#5F95FF",
              fill: "#EFF4FF",
              rx: 6,
              ry: 6,
            },
            text: {
              fontSize: 12,
              fill: "#262626",
            },
          },
          ports: {...ports},
        },
        true
      );

    },
    /**
     * 快捷键与事件
     */
    initEvent() {
      this.fbOptions.flow.graph.on("cell:click", ({e, cell}) => {
        this.hanldeCellClick(cell);
      });
      if (this.isView) {
        return false
      }

      // 添加节点触发
      this.fbOptions.flow.graph.on("node:added", ({node}) => {
        this.handleNodeAdd(node);
      });
      this.fbOptions.flow.graph.on("edge:connected", ({isNew, edge, currentCell}) => {
        if (isNew) {
          this.handleEdgeConnected(edge, currentCell);
        }
      });
      this.fbOptions.flow.graph.on('edge:created', ({ edge }) => {
        this.fbOptions.flow.graph.layout({ nodes: [edge.getSource(), edge.getTarget()] }); // 仅对相关节点进行局部布局
      });
      this.fbOptions.flow.graph.on("node:mouseenter", () => {
        this.showPorts(ports, true);
      });
      this.fbOptions.flow.graph.on("node:mouseleave", () => {
        this.showPorts(ports, false);
      });
      // 连接桩点击
      this.fbOptions.flow.graph.on("node:port:mousedown", (event) => {
        console.log("node:port:mousedown", event);
      });
      this.fbOptions.flow.graph.on("node:port:mouseup", (event) => {
        console.log("node:port:mouseup", event);
      });
      // 删除节点触发
      this.fbOptions.flow.graph.on("node:removed", () => {
        // 删除选中的元素
        const cells = this.fbOptions.flow.graph.getSelectedCells();
        if (cells.length && cells.every(cell => cell.removeBtnShow)) {
          cells.forEach((cell) => {
            this.hanldeNodeDelete(cell);
          });
          this.fbOptions.flow.graph.removeCells(cells);
        }
        this.fbOptions.flow.currentShape = "custom-start";
      });
      this.fbOptions.flow.graph.on("selection:changed", (args) => {
        args.added.forEach((cell) => {
          const notDelete = ['custom-endpoint', 'custom-start', 'custom-end']
          if (!notDelete.some(n => n === cell.shape)) {
            cell['removeBtnShow'] = true;
            cell.addTools([
              {
                name: "button-remove", // 工具名称
              },
            ]);
          }
          if (cell.isEdge()) {
            cell.addTools([
              {
                name: "vertices",
                args: {
                  padding: 4,
                  attrs: {
                    strokeWidth: 0.1,
                    stroke: "#2d8cf0",
                    fill: "#ffffff",
                  },
                },
              },
            ]);
          }
          this.hanldeCellClick(cell);

        });
        args.removed.forEach((cell) => {
          cell.removeTools();
        });
      });
      // 链接桩控制
      this.fbOptions.flow.graph.on("node:mouseenter", () => {
        this.showPorts(true);
      });
      this.fbOptions.flow.graph.on("node:mouseleave", () => {
        this.showPorts(false);
      });

      // 点击画布空白区域
      this.fbOptions.flow.graph.on("blank:click", () => {
        this.fbOptions.flow.currentShape = "custom-start";
        this.fbOptions.flow.graph.getCells().forEach(cell => {
          cell.removeTools()
        })
        this.fbOptions.flow.graph.cleanSelection && this.fbOptions.flow.graph.cleanSelection();
      });

      // copy cut paste
      this.fbOptions.flow.graph.bindKey(["meta+c", "ctrl+c"], () => {
        const cells = this.fbOptions.flow.graph.getSelectedCells();
        if (cells.length) {
          this.fbOptions.flow.graph.copy(cells);
        }
        return false;
      });

      this.fbOptions.flow.graph.bindKey(["meta+x", "ctrl+x"], () => {
        const cells = this.fbOptions.flow.graph.getSelectedCells();
        if (cells.length) {
          this.fbOptions.flow.graph.cut(cells);
        }
        return false;
      });
      this.fbOptions.flow.graph.bindKey(["meta+v", "ctrl+v"], () => {
        if (!this.fbOptions.flow.graph.isClipboardEmpty()) {
          const cells = this.fbOptions.flow.graph.paste({offset: 32});
          this.fbOptions.flow.graph.cleanSelection();
          this.fbOptions.flow.graph.select(cells);
        }
        return false;
      });

      // undo redo
      this.fbOptions.flow.graph.bindKey(["meta+z", "ctrl+z"], () => {
        if (this.fbOptions.flow.graph.canUndo()) {
          this.fbOptions.flow.graph.undo();
        }
        return false;
      });
      this.fbOptions.flow.graph.bindKey(["meta+shift+z", "ctrl+shift+z"], () => {
        if (this.fbOptions.flow.graph.canRedo()) {
          this.fbOptions.flow.graph.redo();
        }
        return false;
      });

      // select all
      this.fbOptions.flow.graph.bindKey(["meta+shift+a", "ctrl+shift+a"], () => {
        const nodes = this.fbOptions.flow.graph.getNodes();
        if (nodes) {
          this.fbOptions.flow.graph.select(nodes);
        }
      });

      // delete
      this.fbOptions.flow.graph.bindKey(["backspace", "delete"], () => {
        // 删除选中的元素
        const cells = this.fbOptions.flow.graph.getSelectedCells();
        console.log("cells", cells);
        if (cells.length && cells.every(cell => cell.removeBtnShow)) {
          cells.forEach((cell) => {
            this.hanldeNodeDelete(cell);
          });
          this.fbOptions.flow.graph.removeCells(cells);
        }
        this.fbOptions.flow.currentShape = "custom-start";
      });

      // zoom
      this.fbOptions.flow.graph.bindKey(["ctrl+1", "meta+1"], () => {
        const zoom = this.fbOptions.flow.graph.zoom();
        if (zoom < 1.5) {
          this.fbOptions.flow.graph.zoom(0.1);
        }
      });
      this.fbOptions.flow.graph.bindKey(["ctrl+2", "meta+2"], () => {
        const zoom = this.fbOptions.flow.graph.zoom();
        if (zoom > 0.5) {
          this.fbOptions.flow.graph.zoom(-0.1);
        }
      });
      // 添加鼠标滚轮缩放监听
      this.fbOptions.flow.graph.on('wheel', (e) => {
        if (!e.domEvent.ctrlKey) return;
        e.preventDefault();
        const delta = e.domEvent.deltaY > 0 ? 0.2 : -0.2;
        this.fbOptions.flow.graph.zoom(this.fbOptions.flow.graph.getZoom() + delta, { x: e.x, y: e.y });
      });
    },
    // 连接桩显示/隐藏
    showPorts(show) {
      const container = document.getElementById("nlbp-container");
      const ports = container.querySelectorAll(".x6-port-body");
      for (let i = 0, len = ports.length; i < len; i = i + 1) {
        ports[i].style.visibility = show ? "visible" : "hidden";
      }
    },
  },
};
</script>

<style lang="scss" scoped>
.step-nlbp {
  display: flex;
  min-height: 500px;
  height: 100%;

  #nlbp-stencil {
    width: 200px;
    height: auto;
    position: relative;
    border-right: 1px solid #dfe3e8;
    background-color: #fff;
  }

  #nlbp-container {
    flex: 1;
    height: 100%;
  }

  .nlbp-draw {
    width: 350px;
    height: 100%;

    .flex-end {
      display: flex;
      justify-content: center;
      margin-top: 16px;
    }
  }
}

.tools-onhover-app {
  display: flex;
  padding: 0;
  font-family: sans-serif;

  .app-content {
    flex: 1;
    height: 320px;
    margin-right: 8px;
    margin-left: 8px;
    box-shadow: 0 0 10px 1px #e9e9e9;
  }
}
</style>
